package com.mf.ergate.dao;

import com.mf.ergate.common.utils.ReflectionUtils;
import com.mf.framework.common.model.Page;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.ibatis.session.RowBounds;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import javax.persistence.Column;
import javax.persistence.Id;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Mybatis-基础DAO
 * User: zhaoming
 * Date: 2018-09-30
 * To change this template use File | Settings | File Templates.
 **/
public class BaseDao<T> extends SqlSessionDaoSupport {

    private Logger logger = LoggerFactory.getLogger(getClass());

    // 批处理操作最大数量
    private static final int MAX_BATCH  = 500;
    // 排序
    private static final String SORTS   = "_sorts";
    // 主键列表
    private static final String IDS     = "_ids";
    // 逻辑主键列表
    private static final String NOS     = "_nos";

    @Resource
    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        super.setSqlSessionTemplate(sqlSessionTemplate);
    }

    /**
     * 新增数据
     *
     * @param statement SQLMapper - Key
     * @param entity    实体对象
     * @return 执行结果
     */
    boolean insert(String statement, T entity) {
        if (entity == null) {
            logger.error("实体对象为空");
            throw new RuntimeException("实体对象为空");
        }
        //自动填充唯一编号
        ReflectionUtils.autoFillUniqueNo(entity);
        return this.getSqlSession().insert(statement, entity) > 0;
    }

    /**
     * 批量新增数据
     *
     * @param statement SQLMapper - Key
     * @param entities  实体对象列表
     * @return 执行结果
     */
    boolean insert(String statement, List<T> entities) {
        if (CollectionUtils.isEmpty(entities)) {
            logger.error("实体对象列表为空");
            throw new RuntimeException("实体对象列表为空");
        }
        if (entities.size() > MAX_BATCH) {
            logger.error("实体对象列表数量已超过最大数量");
            throw new RuntimeException("实体对象列表数量已超过最大数量");
        }
        //自动填充唯一编号
        for (T entity : entities) {
            ReflectionUtils.autoFillUniqueNo(entity);
        }
        return this.getSqlSession().insert(statement, entities) > 0;
    }

    /**
     * 更新数据
     *
     * @param statement SQLMapper - Key
     * @param entity    实体对象
     * @return 执行结果
     */
    boolean update(String statement, T entity) {
        if (entity == null) {
            logger.error("实体对象为空");
            throw new RuntimeException("实体对象为空");
        }
        return this.getSqlSession().update(statement, entity) > 0;
    }

    /**
     * 删除数据
     *
     * @param statement SQLMapper - Key
     * @param obj    对象
     * @return 执行结果
     */
    boolean discard(String statement, Object obj) {
        if (obj == null) {
            logger.error("实体对象为空");
            throw new RuntimeException("实体对象为空");
        }
        return this.getSqlSession().delete(statement, obj) > 0;
    }

    /**
     * 批量删除数据
     *
     * @param statement SQLMapper - Key
     * @param entities  实体对象列表
     * @return 执行结果
     */
    boolean discard(String statement, List<T> entities) {
        if (CollectionUtils.isEmpty(entities)) {
            logger.error("实体对象列表为空");
            throw new RuntimeException("实体对象列表为空");
        }
        if (entities.size() > MAX_BATCH) {
            logger.error("实体对象列表数量已超过最大数量");
            throw new RuntimeException("实体对象列表数量已超过最大数量");
        }
        return this.getSqlSession().delete(statement, entities) > 0;
    }

    /**
     * 根据ID批量删除数据(逻辑删除)
     *
     * @param statement SQLMapper - Key
     * @param entities  实体对象列表
     * @return 执行结果
     */
    boolean deleteByIds(String statement, List<T> entities) {
        if (CollectionUtils.isEmpty(entities)) {
            logger.error("实体对象列表为空");
            throw new RuntimeException("实体对象列表为空");
        }
        if (entities.size() > MAX_BATCH) {
            logger.error("实体对象列表数量已超过最大数量");
            throw new RuntimeException("实体对象列表数量已超过最大数量");
        }

        Map<String, Object> paramMap = new HashMap<>();
        paramMap.putAll(ReflectionUtils.beanToMap(entities.get(0)));
        try {
            List<Object> ids = new ArrayList<>();
            for (Object entity : entities) {
                Field[] fields = entity.getClass().getDeclaredFields();
                for (Field field : fields) {
                    Id idAnnotation = field.getAnnotation(Id.class);
                    if (idAnnotation != null) {
                        field.setAccessible(true);
                        ids.add(field.get(entity));
                        break;
                    }
                }
            }
            paramMap.put(IDS, ids);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return this.getSqlSession().update(statement, paramMap) > 0;
    }

    /**
     * 根据唯一编号批量删除数据(逻辑删除)
     *
     * @param statement SQLMapper - Key
     * @param entities  实体对象列表
     * @return 执行结果
     */
    boolean deleteByUniqueNos(String statement, List<T> entities) {
        if (CollectionUtils.isEmpty(entities)) {
            logger.error("实体对象列表为空");
            throw new RuntimeException("实体对象列表为空");
        }
        if (entities.size() > MAX_BATCH) {
            logger.error("实体对象列表数量已超过最大数量");
            throw new RuntimeException("实体对象列表数量已超过最大数量");
        }

        Map<String, Object> paramMap = new HashMap<>();
        try {
            paramMap.putAll(ReflectionUtils.beanToMap(entities.get(0)));
            List<Object> nos = new ArrayList<>();
            for (Object entity : entities) {
                Field[] fields = entity.getClass().getDeclaredFields();
                for (Field field : fields) {
                    Column columnAnnotation = field.getAnnotation(Column.class);
                    if (columnAnnotation != null && columnAnnotation.unique()) {
                        field.setAccessible(true);
                        nos.add(field.get(entity));
                        break;
                    }
                }
            }
            paramMap.put(NOS, nos);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return this.getSqlSession().update(statement, paramMap) > 0;
    }

    /**
     * 查询一条数据
     *
     * @param statement SQLMapper - Key
     * @param entity    实体对象
     * @return 实体对象
     */
    T selectOne(String statement, Object entity) {
        if (entity == null) {
            logger.error("实体对象为空");
            throw new RuntimeException("实体对象为空");
        }
        return this.getSqlSession().selectOne(statement, entity);
    }

    /**
     * 查询多条数据
     *
     * @param statement SQLMapper - Key
     * @param entity    实体对象
     * @return 实体对象列表
     */
    List<T> selectList(String statement, Object entity) {
        return this.getSqlSession().selectList(statement, entity);
    }

    /**
     * 查询全部数据
     *
     * @param statement SQLMapper - Key
     * @return 实体对象列表
     */
    List<T> selectList(String statement) {
        return this.getSqlSession().selectList(statement);
    }

    /**
     * 统计数据数量
     *
     * @param statement SQLMapper - Key
     * @param entity    实体对象
     * @return 数据数量
     */
    Integer selectCount(String statement, Object entity) {
        return (Integer) this.getSqlSession().selectOne(statement, entity);
    }

    /**
     * 分页查询
     *
     * @param pageStatement  SQLMapper - sqlKey
     * @param countStatement SQLMapper - countKey
     * @param page           分页对象
     * @param entity         实体对象
     * @return 分页对象
     */
    Page<T> selectPage(String pageStatement, String countStatement, Page page, Object entity) {
        if (entity == null) {
            return selectPage(pageStatement, countStatement, page, null);
        } else if (entity instanceof Map) {
            return selectPage(pageStatement, countStatement, page, entity);
        } else {
            return selectPage(pageStatement, countStatement, page, ReflectionUtils.beanToMap(entity));
        }
    }

    /**
     * 分页查询
     *
     * @param pageStatement  SQLMapper - pageKey
     * @param countStatement SQLMapper - countKey
     * @param page           分页对象
     * @param map            参数Map
     * @return 分页对象
     */
    private Page<T> selectPage(String pageStatement, String countStatement, Page page, Map<String, Object> map) {
        //总数
        int totalCount = this.getSqlSession().selectOne(countStatement, map);
        //为0时，返回一个新的page
        if (totalCount == 0) {
            return new Page<>();
        }
        Map<String, Object> parameterMap = new HashMap<>();
        if (map != null) {
            parameterMap.putAll(map);
        }
        //_sorts名固定写法，与PaginationInterceptor中进行映射
        parameterMap.put(SORTS, page.getSortItemMap());
        List<T> list = this.getSqlSession().selectList(pageStatement, parameterMap, new RowBounds((page.getPageNum() - 1) * page.getPageSize(), page.getPageSize()));
        Page<T> result = new Page<>(page.getPageNum(), totalCount, page.getPageSize());
        result.addAll(list);
        return result;
    }
}
